package com.smit.adapter;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager.NameNotFoundException;

import com.smit.common.AppData;
import com.smit.common.AuthControl;
import com.smit.common.FreqTable;
import com.smit.common.HttpUrl;
import com.smit.dvb.DVBFreqInfo;
import com.smit.dvb.DVBTunerInfo;
import com.smit.network.SyncHttpMannager;
import com.smit.utils.FileUtil;
import com.smit.utils.JsonParse;
import com.smit.utils.LogUtil;
import com.smit.utils.MD5Util;
import com.smit.utils.NetUtil;
import com.smit.utils.StrUtil;

import org.apache.http.util.EncodingUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * DVB数据管理，包括DVB本地Jni库的加载和升级，DVB频点，节目别名表
 */
public class DVBDataManager {
    private static final String TAG = "DVBDataManager";
    private static final String PLUGIN_ASSETS_DIR = "icast/plugin/";
    private static final String PLUGIN_NEW_FILE_NAME = "plugin_new.txt";
    private static final String PLUGIN_ASSETS_FILE_NAME = "plugin.txt";
    private static final String BASE_LIB_NAME = "icast_sdk_base_lib";
    private static final String FREQ_TABLE_NAME = "icast_sdk_freq_table";
    private static final String FREQ_TABLE_FILE_NAME = "icast_sdk_freq_table.bin";
    private static final String CHANNEL_ALIAS_NAME = "icast_sdk_channel_alias";
    private static final String CHANNEL_ALIAS_DB_NAME = "icast_sdk_channel_alias.db";
    private static final String PRE_APP_VERSION_CODE = "pre_app_version";
    private static final String PRE_NEXT_NOTIFY_SEARCH_TIME = "next_notify_search_time";
    private static final String FORCE_SEARCH_LEVEL = "force_search_level";
    private static final String OPTIONAL_SEARCH_LEVEL = "optional_search_level";
    private static final String NID_IGNORE = "1";
    private static final String COMMON_PLUGIN = "";
    private static final String COMMON_NETWORK_NAME = "common";
    private static final long NOTIFY_INTERVAL_TIME = 24 * 7 * 3600 * 1000L; //一周
    private Context context;

    private String freqTable = "频点表 (";
    private String pluginLib = "插件库 (";
    private String keyName = "name";
    private String filesDir;
    private String networkName = ""; //插件名，与运营商网络名相同
    private String networkId = NID_IGNORE;
    private final ConcurrentHashMap<String, DVBLibInfo> pluginInfoMap = new ConcurrentHashMap<String, DVBLibInfo>();
    private boolean bBaseLibLoaded = false;
    private boolean bPluginLibLoaded = false;
    private boolean bFreqTableLoaded = false;
    private boolean bChannelAliasLoaded = false;
    private Map<String, String> loadPathMap = new HashMap<String, String>();
    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();

    /**
     * 初始化DVB数据管理
     *
     * @param context Android Application Context
     */
    public DVBDataManager(Context context) {
        this.context = context;
        filesDir = context.getFilesDir().getAbsolutePath();
        //创建files目录
        File file = new File(filesDir, PLUGIN_ASSETS_DIR);
        if (file.exists() == false) {
            file.mkdirs();
        }
        //读取配置文件
        initData();
        //读取运营商网络名和网络ID
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        networkName = settings.getString(AppData.PREF_DVB_NETWORK_NAME, "");
        networkId = settings.getString(AppData.PREF_DVB_NETWORK_ID, "");
        int curAppVersion = getAppVersion();
        int preAppVersion = settings.getInt(PRE_APP_VERSION_CODE, 0);
        if (curAppVersion > preAppVersion) { //如果apk文件更新了，so库可能没有更新，需要删除原来的配置文件，重新加载。
            LogUtil.trace(TAG, "DVBDataManager app update,need clearData first!");
            clearData();
            settings.edit().putInt(PRE_APP_VERSION_CODE, curAppVersion).commit();
            //再次读取配置文件
            LogUtil.trace(TAG, "DVBDataManager initData again!");
            initData();
        }

    }

    /**
     * 初始化插件配置文件数据 1.读取本地保存的plugin_new.txt 2.从assets中读取plugin.txt
     */
    private void initData() {
        LogUtil.trace(TAG, "initData start>>>");
        //读取plugin参数数据，返回给 pluginInfoMap
        try {
            readWriteLock.readLock().lock();
            File file = new File(filesDir + "/" + PLUGIN_ASSETS_DIR, PLUGIN_NEW_FILE_NAME);
            String jsonStr;
            boolean useAssetsPlugin = false;
            if (file.exists()) { //优先读取 files中配置文件
                FileInputStream fileInputStream = new FileInputStream(file);
                int size = fileInputStream.available();
                byte[] buffer = new byte[size];
                fileInputStream.read(buffer);
                fileInputStream.close();
                jsonStr = EncodingUtils.getString(buffer, "UTF-8");
                useAssetsPlugin = false;
            } else { //如果没有就读取assets中文件
                InputStream assets = context.getAssets().open(PLUGIN_ASSETS_DIR + PLUGIN_ASSETS_FILE_NAME);
                int length = assets.available();
                byte[] buffer = new byte[length];
                assets.read(buffer);
                jsonStr = EncodingUtils.getString(buffer, "UTF-8");
                useAssetsPlugin = true;
            }
            JSONObject jsonObject = new JSONObject(jsonStr);
            JSONArray jsonArray = jsonObject.getJSONArray("data");
            int len = jsonArray.length();
            for (int i = 0; i < len; i++) {
                JSONObject object = jsonArray.getJSONObject(i);
                DVBLibInfo plugin;
                if (useAssetsPlugin) {
                    plugin = fromAssetsJson(object);
                } else {
                    plugin = fromFileJson(object);
                }
                //LogUtil.Logd(TAG, plugin.toString()); 
                pluginInfoMap.put(plugin.name, plugin);
            }
        } catch (FileNotFoundException e) {
            LogUtil.trace(TAG, "initData FileNotFoundException");
        } catch (IOException e) {
            LogUtil.trace(TAG, "initData IOException");
        } catch (JSONException e) {
            LogUtil.trace(TAG, "initData JSONException");
        } finally {
            readWriteLock.readLock().unlock();
        }
        LogUtil.trace(TAG, "initData end<<<");
    }

    /**
     * 设置运营商网络名和网络id
     *
     * @param networkName 运营商网络名
     * @param networkId   运营商网络id
     */
    public void setPluginInfo(String networkName, String networkId) {
        this.networkName = networkName;
        this.networkId = networkId;
    }

    /**
     * 加载DVB基础库 libicast_base.so
     */
    public void loadBaseLib() { //加载libicast_base.so
        LogUtil.trace(TAG, "loadBaseLib start>>>");
        String libPath = findLib(BASE_LIB_NAME);
        if (StrUtil.isNullOrEmpty(libPath) == false) {
            LogUtil.error(TAG, "loadBaseLib System.load baseLibPath=" + libPath);
            System.load(libPath); 
            loadPathMap.put(BASE_LIB_NAME, libPath);
            bBaseLibLoaded = true;
            return;
        }
        LogUtil.error(TAG, "loadBaseLib failed");
    }

    /**
     * 加载DVB插件库
     */
    public void loadPluginLib() {
        loadPluginLib(networkName);
    }

    /**
     * @param networkName 运营商网络名
     * @param networkId   运营商网络ID
     */
    public void loadPluginLib(String networkName, String networkId) {
        loadPluginLib(networkName);
        if (this.networkName.equals(networkName) == false) { //运营商改变，再次全部更新一下数据
            this.networkName = networkName;
            this.networkId = networkId;
            updateAllData();
        }
    }

    /**
     * 加载插件so，不同地方不一样，如果没有运营商信息则加载默认的common插件
     *
     * @param networkName 运营商网络名
     */
    public void loadPluginLib(String networkName) { //dvb营运商网络名 = 插件名
        LogUtil.trace(TAG, "loadPluginLib start>>>");
        if (COMMON_NETWORK_NAME.equals(networkName)) {
            LogUtil.debug(TAG, "loadPluginLib SetDvbPluginPath COMMON_PLUGIN");
            DVBAdapter.getInstance().getDVBManager().getDVBService().setDVBPluginPath(COMMON_PLUGIN);
            bPluginLibLoaded = true;
            loadPathMap.put(networkName, COMMON_PLUGIN);
            return;
        }
        String libPath = findLib(networkName);

        if (StrUtil.isNullOrEmpty(libPath) == false) {
            LogUtil.error(TAG, "loadPluginLib SetDvbPluginPath pluginLibPath=" + libPath);
            DVBAdapter.getInstance().getDVBManager().getDVBService().setDVBPluginPath(libPath);
            loadPathMap.put(networkName, libPath);
            bPluginLibLoaded = true;
            updateSearchLevel(networkName);
        } else {
            LogUtil.error(TAG, "loadPluginLib SetDvbPluginPath COMMON_PLUGIN");
            DVBAdapter.getInstance().getDVBManager().getDVBService().setDVBPluginPath(COMMON_PLUGIN);
            loadPathMap.put(networkName, COMMON_PLUGIN);
            bPluginLibLoaded = true;
        }
    }

    /**
     * 加载频道表
     *
     * @throws Exception 读取文件异常
     */
    public void loadFreqTable() throws Exception { //加载频点表
        LogUtil.trace(TAG, "loadFreqTable start>>>");
        FreqTable table = new FreqTable(); // 默认加载内置的table
        String filePath = findLib(FREQ_TABLE_NAME);
        if (StrUtil.isNullOrEmpty(filePath) == false) {
            LogUtil.debug(TAG, "loadFreqTable filePath=" + filePath);
            table = (FreqTable) FileUtil.readObject2(filePath);
        }
        DVBFreqInfo[] frequencyInfos = table.getFreqTable(DVBTunerInfo.TUNER_TYPE_DVBC);
        if (frequencyInfos != null && frequencyInfos.length > 0) {
            DVBAdapter.getInstance().getDVBManager().getSetupControl().setFrequencyTable(frequencyInfos);
            LogUtil.debug(TAG, "loadFreqTable setFrequencyTable ok");
            bFreqTableLoaded = true;
            loadPathMap.put(FREQ_TABLE_NAME, filePath);
            updateSearchLevel(FREQ_TABLE_NAME);
            return;
        }
        LogUtil.error(TAG, "loadFreqTable end<<<");
    }

    /**
     * 加载频点表，有个需求是在扫描运营商之前加载，所有这里有更新和回调
     *
     * @param updateFirst 是否行从网络更新频点表
     * @param callback    设置完频点表后的回调处理
     */
    public void loadFreqTable(boolean updateFirst, final Runnable callback)//需要先更新数据，然后回调扫描运营商
    {
        LogUtil.trace(TAG, "loadFreqTable start>>>");
        LogUtil.debug(TAG, "loadFreqTable updateFirst=" + updateFirst);
        if (updateFirst) {
            //LogUtil.Logd(TAG, "loadFreqTable freqTableUpdateStatus=" + freqTableUpdateStatus); 
            Thread updateFreqTableThread = new Thread(new Runnable() {

                @Override
                public void run() {
                    try {
                        updateFreqTable();
                        loadFreqTable();
                    } catch (Exception e) {
                        LogUtil.debug(TAG, "loadFreqTable Exception");
                    } finally {
                        if (callback != null) {
                            callback.run();
                        }
                    }
                }
            });
            updateFreqTableThread.setName("updateFreqTableThread");
            updateFreqTableThread.setPriority(Thread.MAX_PRIORITY);
            updateFreqTableThread.start();
        } else {
            try {
                loadFreqTable();
            } catch (Exception e) {
                LogUtil.debug(TAG, "loadFreqTable Exception");
            } finally {
                if (callback != null) {
                    callback.run();
                }
            }
        }
        //LogUtil.Logd(TAG, "loadFreqTable freqTableUpdateStatus=" + freqTableUpdateStatus); 
    }

    /**
     * 加载频道别名表
     */
    public void loadChannelAlias() { //设置频道别名
        LogUtil.trace(TAG, "loadChannelAlias start>>>");
        if (bChannelAliasLoaded == true) {
            LogUtil.error(TAG, "loadChannelAlias bChannelAliasLoaded=" + bChannelAliasLoaded);
            return;
        }
        DVBLibInfo info = pluginInfoMap.get(CHANNEL_ALIAS_NAME);
        if (info != null) {
            int dv = info.downloadVersion; //下载版本
            int av = info.assetsVersion; //内置版本
            LogUtil.debug(TAG, "loadChannelAlias download_version=" + dv + " assets_version=" + av);
            if (dv > av) {
                info.assetsVersion = info.downloadVersion; //用来比较，是否需要将下载下来的版本导入数据库
                saveLibInfo();
                final String filePath = findLib(CHANNEL_ALIAS_NAME);
                if (StrUtil.isNullOrEmpty(filePath) == false) {
                    LogUtil.debug(TAG, "loadChannelAlias filePath=" + filePath);
                    loadPathMap.put(CHANNEL_ALIAS_NAME, filePath); 
                    /*Thread updateDBThread = new Thread(new Runnable()
                    { 
                        @Override
                        public void run()
                        { 
                            updateChannelAliasDB(filePath); 
                        }
                    }); 
                    updateDBThread.setName("updateDBThread"); 
                    updateDBThread.start(); 
                    */
                }
            } else {
                LogUtil.debug(TAG, "loadChannelAlias download_path=" + info.downloadPath);
                loadPathMap.put(CHANNEL_ALIAS_NAME, info.downloadPath);
                LogUtil.debug(TAG, "loadChannelAlias download_version==assets_version");
                return;
            }
        }
        LogUtil.error(TAG, "loadChannelAlias end<<<");
    }

    public boolean isBaseLibLoaded() {
        return bBaseLibLoaded;
    }

    public boolean isPluginLibLoaded() {
        return bPluginLibLoaded;
    }

    public boolean isFreqTableLoaded() {
        return bFreqTableLoaded;
    }

    /**
     * 更新baseLib pluginLib freqTable channelAlias
     */
    public void updateAllData() {
        Thread updateDVBDataThread = new Thread(new Runnable() {
            @Override
            public void run() {
                if (StrUtil.isNullOrEmpty(AuthControl.getInstance().getApikWait(20000)) == false) {
                    updateFreqTable();
                    updatePluginLib();
                    updateChannelAlias();
                    updateBaseLib();
                    saveLibInfo();
                    loadChannelAlias();
                }
            }
        });
        updateDVBDataThread.setName("updateDVBDataThread");
        updateDVBDataThread.start();
    }

    /**
     * 设置提示搜台时间间隔，单位为ms
     *
     * @param ms 时间间隔
     */
    public void setNotifySearchInterval(long ms) {
        LogUtil.debug(TAG, "setNotifySearchInterval value=" + ms);
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        settings.edit().putLong(PRE_NEXT_NOTIFY_SEARCH_TIME, System.currentTimeMillis() + ms).apply();
    }

    /**
     * 提示搜台是否可用，超过设置时间间隔后重新可用 1. 提示搜台标志位是否有效 2. 时间是否到期
     *
     * @return true 打开， false 关闭
     */
    public boolean isNotifySearchOn() {
        LogUtil.debug(TAG, "isNotifySearchOn");
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        long nextNotify = settings.getLong(PRE_NEXT_NOTIFY_SEARCH_TIME, 0);
        long now = System.currentTimeMillis();
        LogUtil.debug(TAG, "isNotifySearchOn: nextNotify=" + nextNotify + " now=" + now);
        if (getOptionalSearchLevel() > 0 && nextNotify <= now) {
            LogUtil.debug(TAG, "isNotifySearchOn true");
            return true;
        } else {
            LogUtil.debug(TAG, "isNotifySearchOn false");
            return false;
        }
    }

    /**
     * 清除搜台提示标记
     */
    public void clearNotifySearchFlag() {
        LogUtil.debug(TAG, "clearNotifySearchFlag");
        setOptionalSearchLevel(0);
    }

    /**
     * 强制搜台是否打开
     *
     * @return true 打开， false 关闭
     */
    public boolean isForceSearchOn() {
        LogUtil.debug(TAG, "isForceSearchOn");
        if (getForceSearchLevel() > 0) {
            LogUtil.debug(TAG, "isForceSearchOn true");
            return true;
        } else {
            LogUtil.debug(TAG, "isForceSearchOn false");
            return false;
        }
    }

    /**
     * 清除强制搜台标记
     */
    public void clearForceSearchFlag() {
        LogUtil.debug(TAG, "clearForceSearchFlag");
        setForceSearchLevel(0);
    }

    /**
     * 强制搜台级别，如果大于1，则需要强制搜台
     *
     * @return 强制搜台级别
     */
    private int getForceSearchLevel() {
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        int forceLevel = settings.getInt(FORCE_SEARCH_LEVEL, 0);
        LogUtil.debug(TAG, "getForceSearchLevel forceLevel=" + forceLevel);
        return forceLevel;
    }

    private void setForceSearchLevel(int value) {
        LogUtil.debug(TAG, "setForceSearchLevel value=" + value);
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        settings.edit().putInt(FORCE_SEARCH_LEVEL, value).apply();
    }

    /**
     * 提示搜台级别，如果大于1，则需要提示搜台
     *
     * @return 提示搜台级别
     */
    private int getOptionalSearchLevel() {
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        int optionalLevel = settings.getInt(OPTIONAL_SEARCH_LEVEL, 0);
        LogUtil.debug(TAG, "getOptionalSearchLevel optionalLevel=" + optionalLevel);
        return optionalLevel;
    }

    private void setOptionalSearchLevel(int value) {
        LogUtil.debug(TAG, "setOptionalSearchLevel value=" + value);
        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        settings.edit().putInt(OPTIONAL_SEARCH_LEVEL, value).apply();
    }

    /**
     * 查找文件，这里包括了频点表和频道别名数据:
     * 1.先查看网络下载数据
     * 2.再查本地asset的拷贝
     * 3.再从asset直接拷贝出来
     *
     * @param name lib名称
     * @return 返回lib文件的全路径
     */
    private String findLib(String name) {
        LogUtil.trace(TAG, "findLib start>>>");
        LogUtil.debug(TAG, "findLib name=" + name);
        String libPath = null;
        boolean ret = false;
        try {
            DVBLibInfo libInfo = pluginInfoMap.get(name);
            if (libInfo != null) {
                libPath = libInfo.downloadPath; //网络下载的 lib
                LogUtil.debug(TAG, "findLib download_path=" + libPath);
                ret = checkLibFile(libInfo.downloadPath, libInfo.downloadMD5);
                if (ret == false) {
                    libPath = libInfo.assetsCopyPath; //assets中之前拷贝好的 lib
                    LogUtil.debug(TAG, "findLib assets_copy_path=" + libPath);
                    ret = checkLibFile(libInfo.assetsCopyPath, libInfo.assetsMD5);
                    if (ret == false) { //assets中的lib检查不过：1.首次使用，没有拷贝到files 2.文件损坏
                        libPath = filesDir + "/" + PLUGIN_ASSETS_DIR + libInfo.libname;
                        File file = new File(libPath);
                        if (file.exists()) {
                            file.delete();
                        }
                        FileUtil.copyAssetsFile2Local(context, PLUGIN_ASSETS_DIR + libInfo.libname, libPath);
                        ret = checkLibFile(libPath, libInfo.assetsMD5); //assets中的base lib
                        if (ret == false) {
                            LogUtil.error(TAG, "findLib " + name + " failed");
                            return null;
                        } else {
                            libInfo.assetsCopyPath = libPath; //assets中最新拷贝出来的 lib
                            if (libInfo.assetsVersion == 0) { //如果没有版本号，默认用app的版本，这里频点表和频道别名用的app版本
                                libInfo.assetsVersion = getAppVersion();
                            }
                            saveLibInfo(); //数据改变了需要保存数据
                            LogUtil.debug(TAG, "findLib copy assets ok and assets_copy_path=" + libPath);
                        }
                    }
                }
            }
        } catch (Exception e) {
            LogUtil.error(TAG, "findLib IOException");
            return null;
        }
        LogUtil.trace(TAG, "findLib " + name + " and libPath=" + libPath);
        return libPath;
    }

    /**
     * 检查文件是否存在，并检查文件的md5是否正确，如果没有md5，只检查存在
     *
     * @param filePath 文件全路径
     * @param fileMD5  文件的MD5值，为null就不去检查
     * @return 文件是否正确
     */
    private boolean checkLibFile(String filePath, String fileMD5) {
        String fileMD5New;
        File libFile;
        LogUtil.trace(TAG, "checkLibFile start>>>");
        LogUtil.debug(TAG, "checkLibFile filePath=" + filePath + " fileMD5=" + fileMD5);
        try {
            if (StrUtil.isNullOrEmpty(filePath) == false) {
                libFile = new File(filePath);
                if (libFile.exists()) {
                    fileMD5New = MD5Util.getFileMD5String(libFile);
                    if ((fileMD5New != null && fileMD5New.equalsIgnoreCase(fileMD5))
                            || StrUtil.isNullOrEmpty(fileMD5) == true) { //MD5校验正确  或  没有MD5不需要校验
                        LogUtil.trace(TAG, "checkLibFile ok");
                        return true;
                    }
                }
            }
        } catch (IOException e) {
            LogUtil.error(TAG, "checkLibFile IOException");
        }
        LogUtil.error(TAG, "checkLibFile error");
        return false;
    }

    private void updateBaseLib() {
        updateData(BASE_LIB_NAME, NID_IGNORE);
    }

    private void updatePluginLib() {
        updateData(networkName, networkId); 
        /*
         * 更新后不能离开就提示，需要等加载的时候提示，否则进行搜台，但是加载的是旧的插件
         */
        //updateSearchLevel(networkName); 
    }

    private void updateFreqTable() {
        updateData(FREQ_TABLE_NAME, NID_IGNORE); 
        /*
         * 这里和插件加载不一样，频点表不是每次都加载，只有第一次扫描运营商会。
         * 所以在更新频点表以后就要更新搜台标记，现在设计是每次起来都会更新
         * 频点表，但是不会去加载频点表；而且只有在没有营运商信息的时候才提示搜台或
         * 强制搜台，有运营商的情况不需要提示。
         */
        if (StrUtil.isNullOrEmpty(networkName) || COMMON_NETWORK_NAME.equals(networkName)) {
            updateSearchLevel(FREQ_TABLE_NAME);
        }
    }

    private void updateChannelAlias() {
        boolean ret = updateData(CHANNEL_ALIAS_NAME, NID_IGNORE);
        if (ret == true) { //有新的数据，需要重新加载
            bChannelAliasLoaded = false;
        }
    }

    /**
     * 更新别名数据到本地数据库
     * @param filePath 别名数据库
     */
    /*
    private void updateChannelAliasDB(String filePath)
    { 
        LogUtil.trace(TAG, "updateChannelAliasDB start>>>!"); 
        //把本地数据清空， 导入新的数据
        AliasTableDAO.getInstance().clearTable(); 
        SQLiteDatabase channelAliasDB = AliasTableDAO.getInstance().getSqliteBaseUtil().getDbHelper()
                .getReadableDatabase(); 
        SQLiteDatabase downloadDB = SQLiteDatabase.openOrCreateDatabase(filePath, null); 
        String tableName = "channel"; 
        channelAliasDB.beginTransaction(); 
        try
        { 
            Cursor cursor = downloadDB.query(true, tableName, null, null, null, null, null, null, null); 
            ContentValues values = new ContentValues(); 
            while (cursor.moveToNext())
            { 
                values.clear(); 
                int count = cursor.getColumnCount(); 
                for (int i = 0;  i < count;  i++)
                { 
                    String name = cursor.getColumnName(i); 
                    if (cursor.getType(i) == Cursor.FIELD_TYPE_STRING)
                    { 
                        values.put(name, cursor.getString(i)); 
                    }
                    else if (cursor.getType(i) == Cursor.FIELD_TYPE_INTEGER)
                    { 
                        values.put(name, cursor.getInt(i)); 
                    }
                }
                //LogUtil.Logd(TAG, "updateChannelAliasDB: values=" + values.toString()); //打印导致时间太长
                channelAliasDB.insert(tableName, null, values); 
            }
            cursor.close(); 
            channelAliasDB.setTransactionSuccessful(); 
        }
        catch (Exception e)
        { 
            LogUtil.trace(TAG, "updateChannelAliasDB Exception!!!"); 
            e.printStackTrace(); 
        }
        finally
        { 
            // 结束事务
            channelAliasDB.endTransaction(); 
            //channelAliasDB.close(); //单例模式不需要关闭
        }
        LogUtil.trace(TAG, "updateChannelAliasDB end<<<"); 
        bChannelAliasLoaded = true; 
    }
*/

    /**
     * 更新强制搜台和提示搜台的状态，
     * 这里将 assets_force_level 设置为 download_force_level
     * +1是为了保证下次进来不会再次打开
     *
     * @param name 插件名
     */
    private void updateSearchLevel(String name) {
        DVBLibInfo info = pluginInfoMap.get(name);
        boolean dataChanged = false;
        LogUtil.trace(TAG, "updateSearchLevel start>>>");
        if (info != null) {
            if (info.downloadForceLevel > info.assetsForceLevel) {
                setForceSearchLevel(info.downloadForceLevel - info.assetsForceLevel);
                //这里是为了后面来知道到底是哪个的force(频点表，插件)，正常直接等于就好了，不用倒过来
                info.downloadForceLevel = -info.downloadForceLevel;
                dataChanged = true;
            }
            if (info.downloadOptionalLevel > info.assetsOptionalLevel) {
                setOptionalSearchLevel(info.downloadOptionalLevel - info.assetsOptionalLevel);
                //这里是为了后面来知道到底是哪个的optional(频点表，插件)，正常直接等于就好了，不用倒过来
                info.downloadOptionalLevel = -info.downloadOptionalLevel;
                dataChanged = true;
            }
        }
        if (dataChanged) {
            saveLibInfo();
        }
        LogUtil.trace(TAG, "updateSearchLevel end<<<");
    }

    /**
     * 根据名称和id来更新网络数据，只有插件需要id，其他的只需要名称
     *
     * @param name 需要更新的数据的名称
     * @param id   需要更新的数据的ID
     * @return 是否有更新
     */
    private synchronized boolean updateData(String name, String id) {
        LogUtil.trace(TAG, "updateData start>>>");
        if (NetUtil.isNetworkOk(context) == false) {
            LogUtil.error(TAG, "updateData isNetworkOk=false");
            return false;
        }
        String apik = AuthControl.getInstance().getApikWait(20000); //这里时间长一点ms
        JSONObject jsonObject;
        HashMap<String, String> dataMap;
        SyncHttpMannager httpMannager = new SyncHttpMannager();
        HashMap<String, String> hashMap = new HashMap<String, String>();
        hashMap.put("apik", apik);
        hashMap.put("netname", name);
        hashMap.put("netid", id);
        hashMap.put("sdk_version_name", AppData.SDK_VERSION_NAME);
        hashMap.put("sdk_version_code", String.valueOf(AppData.SDK_VERSION_CODE));
        hashMap.put("sdk_api_level", String.valueOf(AppData.SDK_API_LEVEL));
        hashMap.put("sdk_api_revision", String.valueOf(AppData.SDK_API_REVISION));

        LogUtil.debug(TAG, "updateData name=" + name + " id=" + id + " apik=" + apik);
        LogUtil.debug(TAG, "updateData network=" + NetUtil.isNetworkOk(context));
        if (NetUtil.isNetworkOk(context) && StrUtil.isNullOrEmpty(apik) == false) {
            jsonObject = httpMannager.doSyncGetJson(HttpUrl.getUrl(HttpUrl.DVB_PLUGIN), hashMap);

            if (jsonObject != null) {
                //LogUtil.Logd(TAG, "updateData jsonObject " + jsonObject.toString()); 
                dataMap = JsonParse.parseDVBData(jsonObject);
                if (dataMap != null) {
                    String downloadUrl = dataMap.get("url");
                    String fileMD5 = dataMap.get("md5");
                    String forceLevel = dataMap.get("force");
                    String optionalLevel = dataMap.get("optional");
                    int newVersion = StrUtil.parseInt(dataMap.get("version"));
                    DVBLibInfo dataInfo = pluginInfoMap.get(name);
                    int localVersion = 0;
                    if (dataInfo != null) { //本地有记录
                        int dv = dataInfo.downloadVersion; //下载版本
                        int av = dataInfo.assetsVersion; //内置版本
                        localVersion = dv > av ? dv : av;
                    } else { //本地没有记录的，新的数据，可能是新的地方，有新的营运商
                        dataInfo = new DVBLibInfo();
                        dataInfo.name = name;
                        dataInfo.libname = name;
                        dataInfo.nid = id;
                        if (CHANNEL_ALIAS_NAME.equals(name) || FREQ_TABLE_NAME.equals(name)) { //如果是频道别名，版本号为app版本
                            localVersion = getAppVersion();
                            dataInfo.assetsVersion = localVersion;
                        }
                        pluginInfoMap.put(dataInfo.name, dataInfo);
                    }
                    LogUtil.debug(TAG, "updateData newVersion=" + newVersion + " localVersion=" + localVersion);
                    if (newVersion > localVersion) { //下载文件
                        String downloadPath = filesDir + "/" + PLUGIN_ASSETS_DIR + dataInfo.libname + "."
                                + newVersion;
                        File file = new File(downloadPath);
                        if (file.exists()) {
                            file.delete();
                        }
                        boolean ret = downloadFile(downloadUrl, downloadPath);
                        LogUtil.trace(TAG, "updateData downloadFile ret=" + ret);
                        if (ret == true) { //检查下载是否正确
                            ret = checkLibFile(downloadPath, fileMD5);
                            if (ret == true) {
                                File filePrevDownload = new File(dataInfo.downloadPath);
                                if (filePrevDownload.exists()) {
                                    //fileOld.delete(); //这里不能删除，启动的时候已经加载了旧的版本了,保存到asset属性下
                                    File fileAssets = new File(dataInfo.assetsCopyPath); //删除原来的文件。
                                    if (fileAssets.exists()) {
                                        fileAssets.delete();
                                    }
                                    dataInfo.assetsCopyPath = dataInfo.downloadPath;
                                    dataInfo.assetsForceLevel = Math.abs(dataInfo.downloadForceLevel);
                                    dataInfo.assetsVersion = dataInfo.downloadVersion;
                                    dataInfo.assetsMD5 = dataInfo.downloadMD5;
                                    dataInfo.assetsOptionalLevel = Math.abs(dataInfo.downloadOptionalLevel);
                                }
                                dataInfo.downloadMD5 = fileMD5;
                                dataInfo.downloadPath = downloadPath;
                                dataInfo.downloadVersion = newVersion;
                                dataInfo.downloadForceLevel = StrUtil.parseInt(forceLevel);
                                dataInfo.downloadOptionalLevel = StrUtil.parseInt(optionalLevel);
                                LogUtil.trace(TAG, "updateData downloadFile ok and check Ok");
                                LogUtil.trace(TAG, "updateData name=" + name + " end<<<");
                                return true;
                            }
                        }
                    }
                }
            }
        }
        LogUtil.trace(TAG, "updateData name=" + name + " end<<<");
        return false;
    }

    /**
     * @param url
     * @param saveFilePath
     * @return boolean
     * @Title: downloadFile
     * @Description: 下载文件到指定目录
     */
    private boolean downloadFile(String url, String saveFilePath) {
        boolean ret = false;
        LogUtil.trace(TAG, "downloadFile start>>>");
        LogUtil.debug(TAG, "downloadFile saveFilePath=" + saveFilePath);
        try {
            readWriteLock.writeLock().lock();
            SyncHttpMannager syncHttpMannager = new SyncHttpMannager();
            ret = syncHttpMannager.doSyncDownload(url, saveFilePath);
        } finally {
            readWriteLock.writeLock().unlock();
        }
        return ret;
    }

    /**
     * 从asset文件中的json数据构建 DVBLibInfo，asset为apk默认的
     * @param jsonObject json数据
     * @return 返回DVBLibInfo对象
     */
    DVBLibInfo fromAssetsJson(JSONObject jsonObject) {
        DVBLibInfo pluginInfo = new DVBLibInfo();
        pluginInfo.libname = jsonObject.optString("libname");
        pluginInfo.name = jsonObject.optString(keyName);
        pluginInfo.nid = jsonObject.optString("nid");
        pluginInfo.magicid = jsonObject.optString("magicid");
        pluginInfo.assetsVersion = jsonObject.optInt("v");
        pluginInfo.assetsMD5 = jsonObject.optString("md5");

        pluginInfo.assetsForceLevel = jsonObject.optInt("force_level");
        pluginInfo.assetsOptionalLevel = jsonObject.optInt("optional_level");
        return pluginInfo;
    }

    /**
     * 从plugin_new.txt文件中的json数据构建 DVBLibInfo,plugin_new.txt为程序运行后生成
     * @param jsonObject json数据
     * @return 返回DVBLibInfo对象
     */
    DVBLibInfo fromFileJson(JSONObject jsonObject) {
        DVBLibInfo pluginInfo = new DVBLibInfo();
        pluginInfo.libname = jsonObject.optString("libname");
        pluginInfo.name = jsonObject.optString(keyName);
        pluginInfo.nid = jsonObject.optString("nid");
        pluginInfo.magicid = jsonObject.optString("magicid");
        pluginInfo.assetsCopyPath = jsonObject.optString("assets_copy_path");
        pluginInfo.assetsVersion = jsonObject.optInt("assets_version");
        pluginInfo.assetsMD5 = jsonObject.optString("assets_md5");

        pluginInfo.downloadPath = jsonObject.optString("download_path");
        pluginInfo.downloadVersion = jsonObject.optInt("download_version");
        pluginInfo.downloadMD5 = jsonObject.optString("download_md5");
        pluginInfo.assetsForceLevel = jsonObject.optInt("assets_force_level");
        pluginInfo.assetsOptionalLevel = jsonObject.optInt("assets_optional_level");
        pluginInfo.downloadForceLevel = jsonObject.optInt("download_force_level");
        pluginInfo.downloadOptionalLevel = jsonObject.optInt("download_optional_level");
        pluginInfo.desc = jsonObject.optString("desc");
        return pluginInfo;
    }

    /**
     * @return void
     * @Title: saveLibInfo
     * @Description: 保存新的配置数据到plugin_new.txt
     */
    private void saveLibInfo() {
        LogUtil.trace(TAG, "saveLibInfo start>>>");
        if (pluginInfoMap != null && pluginInfoMap.size() > 0) {
            try {
                readWriteLock.writeLock().lock();
                JSONObject jsonObject = new JSONObject();
                JSONArray jsonArray = new JSONArray();
                for (String key : pluginInfoMap.keySet()) {
                    DVBLibInfo info = pluginInfoMap.get(key);
                    JSONObject object = info.toJsonObject();
                    jsonArray.put(object);
                }
                jsonObject.putOpt("data", jsonArray);
                FileUtil.writeString2(context, filesDir + "/" + PLUGIN_ASSETS_DIR
                        + PLUGIN_NEW_FILE_NAME, jsonObject.toString(4));
                LogUtil.trace(TAG, "saveLibInfo end<<<");
            } catch (JSONException e) {
                LogUtil.error(TAG, "saveLibInfo JSONException");
            } catch (Exception e) {
                LogUtil.error(TAG, "saveLibInfo Exception");
            } finally {
                readWriteLock.writeLock().unlock();
                LogUtil.trace(TAG, "saveLibInfo end<<<");
            }
        }
    }

    /**
     * 删除文件
     * @param filePath 文件全路径
     */
    void deleteFile(String filePath) {
        File file = new File(filePath);
        if (file.exists()) {
            file.delete();
            if (file.exists())//正在被引用，没有删掉
            {
                file.deleteOnExit();
            }
        }
    }

    /**
     * @return int
     * @Title: getAppVersion
     * @Description: 获取app的版本，频道别名和频点表的版本以此为参考
     */
    private int getAppVersion() {
        try {
            if (context != null) {
                PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
                LogUtil.trace(TAG, "getAppVersion versionCode=" + info.versionCode);
                return info.versionCode;
            }
        } catch (NameNotFoundException e) {
            LogUtil.error(TAG, "getAppVersion Exception:" + e.getMessage());
        }
        return 0;
    }

    /**
     * @return String
     * @Title: whichForce
     * @Description: 具体是哪个强制搜台标记 F代表频点表的，P代表插件的
     */
    private String whichForce() {
        String tag = "";
        DVBLibInfo info = pluginInfoMap.get(FREQ_TABLE_NAME);
        LogUtil.trace(TAG, "whichForce start>>>");
        int diff = 0;
        int assetsForceLevel = info.assetsForceLevel;
        if (info != null) {
            //这里不应该有小于的情况，是在updateSearchLevel做了手脚，为了在这里判断，请不用误解！！！
            diff = Math.abs(info.downloadForceLevel) - assetsForceLevel;
            if (diff >= 0) {
                tag += freqTable + assetsForceLevel + "->" + Math.abs(assetsForceLevel) + ")";
            } else {
                tag += freqTable + assetsForceLevel + ")";
            }
        }
        info = pluginInfoMap.get(networkName);
        LogUtil.trace(TAG, "whichForce networkName=" + networkName);
        if (info != null) {
            //这里不应该有小于的情况，是在updateSearchLevel做了手脚，为了在这里判断，请不用误解！！！
            diff = Math.abs(info.downloadForceLevel) - assetsForceLevel;
            if (diff > 0) {
                tag += pluginLib + assetsForceLevel + "->" + Math.abs(assetsForceLevel) + ")";
            } else {
                tag += pluginLib + assetsForceLevel + ")";
            }
        }
        return tag;
    }

    /**
     * 具体是哪个提示搜台标记 F代表频点表的，P代表插件的
     *
     * @return 返回描述字符串
     */
    private String whichOptional() {
        String tag = "";
        DVBLibInfo info = pluginInfoMap.get(FREQ_TABLE_NAME);
        LogUtil.trace(TAG, "whichForce start>>>");
        int diff = 0;
        if (info != null) {
            //这里不应该有小于的情况，是在updateSearchLevel做了手脚，为了在这里判断，请不要误解！！！
            diff = Math.abs(info.downloadOptionalLevel) - info.assetsOptionalLevel;
            if (diff >= 0) {
                tag += freqTable + info.assetsOptionalLevel + "->" + Math.abs(info.downloadOptionalLevel) + ")";
            } else {
                tag += freqTable + info.assetsOptionalLevel + ")";
            }
        }
        info = pluginInfoMap.get(networkName);
        LogUtil.trace(TAG, "whichForce networkName=" + networkName);
        if (info != null) {
            //这里不应该有小于的情况，是在updateSearchLevel做了手脚，为了在这里判断，请不要误解！！！
            diff = Math.abs(info.downloadOptionalLevel) - info.assetsOptionalLevel;
            if (diff >= 0) {
                tag += pluginLib + info.assetsOptionalLevel + "->" + Math.abs(info.downloadOptionalLevel) + ")";
            } else {
                tag += pluginLib + info.assetsOptionalLevel + ")";
            }
        }
        return tag;
    }

    /**
     * @return 返回上面具体的描述信息
     * @Description: 获取插件管理的一些数据信息，显示到UI上，
     * 方便调试和测试 有下面一些信息：
     * 运营商网络名
     * 网络ID
     * 强制搜台标记
     * 提示搜台标记
     * base库最新版本和加载版本
     * 插件库最新版本和加载版本
     * 频点表最新版本和加载版本
     * 频道别名最新版本和加载版本
     */
    public String getDataVersionInfo() {
        String veriosnStr = "";
        veriosnStr += "Name=" + networkName + " Id=" + networkId + "\n";
        //veriosnStr += "新的插件未加载或已经搜过台则下面为false，\n后面是插件中级别变化信息\n"; 
        veriosnStr += "强制搜台：" + isForceSearchOn() + " [ " + whichForce() + " ]\n";
        veriosnStr += "提示搜台：" + isNotifySearchOn() + " [" + whichOptional() + " ]\n";
        for (String key : pluginInfoMap.keySet()) {
            DVBLibInfo info = pluginInfoMap.get(key);
            String item = "";
            LogUtil.debug(TAG, "getDataVersionInfo info:" + info.toString());
            LogUtil.debug(TAG, "getDataVersionInfo path=" + loadPathMap.get(info.name));
            int dv = info.downloadVersion; //下载版本
            int av = info.assetsVersion; //内置版本
            //当前最新的版本
            if (dv >= av && StrUtil.isNullOrEmpty(info.downloadPath) == false) {
                item += info.name + "： latest=" + info.downloadVersion;

            } else if (av > 0 && StrUtil.isNullOrEmpty(info.assetsCopyPath) == false) {
                item += info.name + "： latest=" + info.assetsVersion;
            }
            if (StrUtil.isNullOrEmpty(item) == true) {
                continue;
            }
            //当前加载的版本
            if (StrUtil.isNullOrEmpty(info.downloadPath) == false
                    && info.downloadPath.equals(loadPathMap.get(info.name))) {
                item += " load=" + info.downloadVersion;
                item += "\n";
            } else if (StrUtil.isNullOrEmpty(info.assetsCopyPath) == false
                    && info.assetsCopyPath.equals(loadPathMap.get(info.name))) {
                item += " load=" + info.assetsVersion;
                item += "\n";
            } else {
                if (StrUtil.isNullOrEmpty(item) == false) {
                    item += " load=NO";
                    item += "\n";
                }
            }
            veriosnStr += item;
        }
//        if (networkName.equals(CDVB.COMMON_NETWORK_NAME))
//        { 
//            veriosnStr += "plugin： load=common"; 
//        }
//        veriosnStr += "App Version=" + StrUtil.getCurrentVersionName(); 
        return veriosnStr;
    }

    /**
     * 清除所有保存的数据，还原为应用默认数据
     */
    public void clearData() {
        LogUtil.trace(TAG, "clearData start>>>");

        SharedPreferences settings = context.getSharedPreferences(AppData.PREF_SHARE_NAME, Context.MODE_PRIVATE);
        settings.edit().remove(PRE_APP_VERSION_CODE);
        for (String key : pluginInfoMap.keySet()) {
            DVBLibInfo info = pluginInfoMap.get(key);
            deleteFile(info.assetsCopyPath);
            deleteFile(info.downloadPath);
        }
        deleteFile(filesDir + "/" + PLUGIN_ASSETS_DIR + PLUGIN_NEW_FILE_NAME); //配置文件
        LogUtil.trace(TAG, "clearData end<<<");
    }

    /**
     * 序列化保存本工程中的com.smit.dvb.FrequencyTable对象，
     * 可以修改这个类以后保存，作为升级文件放到服务器
     */
    public void serializeFreqTable() {
        try {
            LogUtil.info(TAG, "serializeFreqTable start");
            FreqTable table = new FreqTable();
            FileUtil.writeObject(context, "freq_table_v1", table);
        } catch (Exception e) {
            LogUtil.error(TAG, "serializeFreqTable Exception");
        }
    }

    /**
     * DVB 插件和数据
     */
    class DVBLibInfo {
        String libname = ""; //"libicast_base.so",
        String name = "";  //"libicast_base",
        String nid = "1";  //"1",
        String magicid = "0"; //"0",
        String opid = "1"; //"",
        String assetsCopyPath = ""; //
        int assetsVersion = 0; //"7",
        String assetsMD5 = "";
        String downloadPath = "";
        int downloadVersion = 0;
        String downloadMD5 = "";
        int assetsForceLevel = 0; //强制搜台
        int assetsOptionalLevel = 0; //提示搜台
        int downloadForceLevel = 0; //强制搜台
        int downloadOptionalLevel = 0; //提示搜台
        String desc = ""; //"描述"

        /**
         * DVBLibInfo 转json 对象
         * @return json object
         */
        public JSONObject toJsonObject() {
            JSONObject jsonObject = new JSONObject();
            try {
                jsonObject.put("libname", libname);
                jsonObject.put(keyName, name);
                jsonObject.put("nid", nid);
                jsonObject.put("magicid", magicid);
                jsonObject.put("assets_copy_path", assetsCopyPath);
                jsonObject.put("assets_version", assetsVersion);
                jsonObject.put("assets_md5", assetsMD5);
                jsonObject.put("download_path", downloadPath);
                jsonObject.put("download_version", downloadVersion);
                jsonObject.put("download_md5", downloadMD5);
                jsonObject.put("assets_force_level", assetsForceLevel);
                jsonObject.put("assets_optional_level", assetsOptionalLevel);
                jsonObject.put("download_force_level", downloadForceLevel);
                jsonObject.put("download_optional_level", downloadOptionalLevel);
                jsonObject.put("desc", desc);
            } catch (JSONException e) {
                LogUtil.error(TAG, "toJsonObject JSONException");
            }
            return jsonObject;
        }

        /**
         * DVBLibInfo 转 json string
         * @return string 对象
         */
        public String toString() {
            try {
                return toJsonObject().toString(4);
            } catch (JSONException e) {
                LogUtil.error(TAG, "toString JSONException");
            }
            return "";
        }
    }
}